# -*- shell-script -*-

# 20sysfs - Hardware database scanning routines and variables for sysfs.

# This file is part of the Linux lsvpd package.

# (C) Copyright IBM Corp. 2002, 2003, 2004, 2005

# Maintained by Martin Schwenke <martins@au.ibm.com>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    
# $Id: 20sysfs,v 1.1 2006/04/11 18:38:28 emunson Exp $

sysfs_dir=$(sed -n -e 's@^[^ ]* \([^ ]*\) sysfs .*@\1@p' /proc/mounts)

[ -n "$sysfs_dir" ] || return 0

######################################################################

source_root="$sysfs_dir"

list_adapters ()
{
    # Sets: adapter_list
    adapter_list=""

    local bus_type
    for bus_type in "pci" "pseudo" "vio" ; do
	sysfs_list_adapters_by_bus "$bus_type"
    done

    sysfs_list_adapters_ide_misc
}

sysfs_list_adapters_by_bus ()
{
    # Sets: adapter_list

    local bus_type="$1"

    local devices_dir="${sysfs_dir}/bus/${bus_type}/devices"
    local n
    for n in "${devices_dir}/"* ; do
	local bus_addr="${n#${devices_dir}/}"
	adapter_list="${adapter_list} ${bus_type}/${bus_addr}"
	
	# FIXME: have lost channels!
    done
}

sysfs_list_adapters_ide_misc ()
{
    # Sets: adapter_list

    local n
    for n in "${sysfs_dir}/devices/"ide* ; do
	local adapter_info="unknown/${n#${sysfs_dir}/devices/}"
	adapter_list="${adapter_list} ${adapter_info}"
    done
}

######################################################################

unset -f get_source_node_adapter
unset -f get_source_node_device

get_source_node_DEFAULT ()
{
    # Sets: source_node
    source_node=""

    local meta_type="$1"
    local node_type="$2"
    local bus_info="$3"
    local subtype="$4"

    local sysfs_device_node
    get_sysfs_device_node "${bus_info%/*}" "${bus_info#*/}"

    source_node="$sysfs_device_node"
}

######################################################################

get_names_adapter ()
{
    # Sets: names
    names=""

    local bus_type="$1"
    local bus_addr="$2"

    local subtype
    retrieve_subtype "$bus_type" "$bus_addr"
    [ -n "$subtype" ] || return 1 ### !!!
    
    local subdirs
    get_sysfs_device_node_subdirs "$bus_type" "$bus_addr" "$subtype" || \
	return 1  ### !!!

    if [ -n "$subdirs" ] ; then
	names="$subdirs"
    else
	# Some adapters, like ethernet won't have named
	# subdirectories.  The only way to find the name is to find
	# the class node.

	# FIXME: This code replaces code that finds the first exact
	# match for class node's device link.  This code finds any
	# match and then checks it is exact.  I think the 2 cases will
	# be identical in practice, but I'm not 100% sure.  If this
	# turns out not to be true, copy the code from
	# get_sysfs_class_node here and change the prefix match to an
	# exact match.
	#
	# We might want to change this to process all the possible
	# class nodes.

	local sysfs_device_node
	get_sysfs_device_node "$bus_type" "$bus_addr"
	[ -n "$sysfs_device_node" ] || return 1  ### !!!

	local sysfs_class_node
	get_sysfs_class_node "$bus_type" "$bus_addr" "$subtype"

	if [ -n "$sysfs_class_node" ] ; then
	    local t="${sysfs_class_node}/device"
	    if [ -L "$t" ] && cd -P "$t" ; then
		[ "$PWD" = "${sysfs_device_node}" ] && \
		    names="${sysfs_class_node##*/}"  # basename
		cd "$OLDPWD"
	    fi
	fi
    fi
}

######################################################################

pci_get_config_filename ()
{
    # Sets: pci_config
    pci_config=""

    local pci_addr="$1"

    local conf="${sysfs_dir}/bus/pci/devices/${pci_addr}/config"
    [ -f "$conf" ] && pci_config="$conf"
}

######################################################################

adapter_extra_vpd_hooks="${adapter_extra_vpd_hooks} sysfs_fw_version_vpd_hook"

sysfs_fw_version_vpd_hook ()
{
    # Sets: vpd_function key val
    vpd_function="" ; key="" ; val=""

    local bus_type="$1"
    local bus_addr="$2"
    local subtype="$3"

    local sysfs_class_node
    get_sysfs_class_node "$bus_type" "$bus_addr" "$subtype"

    if [ -n "$sysfs_class_node" ] ; then
	local a
	for a in "fw_version" "fwrev" ; do
	    local a="${sysfs_class_node}/${a}"
	    if [ -r "$a" ] ; then
		vpd_function=vpd_field_override_link
		key="RM"
		val="$a"
		break
	    fi
	done
    fi
}

######################################################################

adapter_retrieve_logicals_vpd ()
{
    local bus_type="$1"
    local bus_addr="$2"
    local node="$3"

    local subtype
    retrieve_subtype_basic "$node"
    [ -n "$subtype" ] || return 1  ### !!!
    
    local subdirs
    get_sysfs_device_node_subdirs "$bus_type" "$bus_addr" "$subtype" || \
	return 1  ### !!!

    # If there's more than one subdirectory, remember the details of
    # channels and subdirectories in lsvpd,logicals.
    if [ "$subdirs" != "${subdirs#* }" ] ; then
	local num=0
	local f="${node}/lsvpd,logicals"
	>"$f"
	local i
	for i in $subdirs ; do
	    echo "${num} ${i}" >>"$f"
	    num=$(($num + 1))
	done
    fi

    adapter_retrieve_logicals_common \
	"$bus_type" "$bus_addr" "$subtype" "$node" "$subdirs"
}

######################################################################

get_yl_adapter ()
{
    # Sets: yl

    local bus_type="$1"
    local bus_addr="$2"
    local source_node="$3"
    local node="$4"

    yl="$bus_addr"

    case "$source_node" in
	(*/pseudo*)      yl="~${yl}" ;;
	(*/devices/ide*) yl="?${yl}" ;;
    esac
}

######################################################################

get_sysfs_device_node ()
{
    # Sets: sysfs_device_node

    local bus_type="$1"
    local bus_addr="$2"

    local bus_alias
    bus_alias_get "${bus_type}/${bus_addr}" "sysfs-device"
    sysfs_device_node="$bus_alias"

    [ -n "$sysfs_device_node" ] && return 0 ### !!!

    local t
    case "$bus_type" in
	(unknown) t=""                 ;;
	(*)       t="/bus/${bus_type}" ;;
    esac
    t="${sysfs_dir}${t}/devices/${bus_addr}"

    if [ -L "$t" ] && cd -P "$t" ; then
	sysfs_device_node="$PWD"
	cd "$OLDPWD"
    elif [ -d "$t" ] ; then
	sysfs_device_node="$t"
    fi

    if [ -n "$sysfs_device_node" -a -d "$sysfs_device_node" ] ; then
	case "$sysfs_device_node" in
	    "${sysfs_dir}/devices/"*)
		bus_alias_set "${bus_type}/${bus_addr}" \
		    "$sysfs_device_node" "sysfs-device"
		;;
	    *)
		: "Unknown device path format."
		sysfs_device_node=""
		;;
	esac
    fi
}

get_sysfs_device_node_subdirs ()
{
    # Sets: subdirs
    subdirs=""

    local bus_type="$1"
    local bus_addr="$2"
    local subtype="$3"

    local sysfs_device_node
    get_sysfs_device_node "$bus_type" "$bus_addr"
    [ -n "$sysfs_device_node" ] ||  return 1 ### !!!
    
    local os_prefix
    adapter_set_os_prefix "$subtype"
    [ -n "$os_prefix" ] || return 1  ### !!!
    
    case "$bus_type" in
	unknown)
	    sysfs_device_node="${sysfs_device_node%/*}"  # dirname
	    ;;
    esac

    # FIXME: limit of 100000 adapters!  :-)
    cd -P "$sysfs_device_node"
    local i
    for i in "$os_prefix"[0-9] "$os_prefix"[0-9][0-9] \
	"$os_prefix"[0-9][0-9][0-9] "$os_prefix"[0-9][0-9][0-9][0-9] \
	"$os_prefix"[0-9][0-9][0-9][0-9][0-9] ; do
	[ -d "$i" ] && \
	    subdirs="${subdirs} ${i}"
    done
    subdirs="${subdirs# }" # Remove "accidently" added leading space.
    cd "$OLDPWD"
}

######################################################################

set_sysfs_adapter_class ()
{
    # Sets: sysfs_adapter_class
    sysfs_adapter_class=""

    local subtype="$1"

    case "$subtype" in
	scsi|sata|ata|iscsi|fibre|raid|ibmvscsi)
	    sysfs_adapter_class="scsi_host"
	    ;;
	ethernet|ibmveth|token)
	    sysfs_adapter_class="net"
	    ;;
	(usb)      sysfs_adapter_class="usb_host"  ;;
	(sound)    sysfs_adapter_class="sound"     ;;
	# How about "display" and "serial"?
    esac
}

# FIXME: For current purposes this is OK, but it only really gets the
# first loose match for the sysfs_device_node.
get_sysfs_class_node ()
{
    # Sets: sysfs_class_node

    local bus_type="$1"
    local bus_addr="$2"
    local subtype="$3"

    local bus_alias
    bus_alias_get "${bus_type}/${bus_addr}" "sysfs-class"
    sysfs_class_node="$bus_alias"

    [ -n "$sysfs_class_node" ] && return 0 ### !!!

    local sysfs_device_node
    get_sysfs_device_node "$bus_type" "$bus_addr"

    local sysfs_adapter_class
    set_sysfs_adapter_class "$subtype"
    
    if [ -n "$sysfs_adapter_class" ] ; then
	local d="${sysfs_dir}/class/${sysfs_adapter_class}"
	local i
	for i in "${d}/"* ; do
	    local t="${i}/device"
	    if [ -L "$t" ] && cd -P "$t" ; then
		if [ "$PWD" != "${PWD#${sysfs_device_node}}" ] ; then
		    # $sysfs_device_node is a prefix of $PWD.
		    sysfs_class_node="$i"
		    cd "$OLDPWD"
		    break
		fi
		cd "$OLDPWD"
	    fi
	done
    fi

    [ -d "$sysfs_class_node" ] && \
	bus_alias_set "${bus_type}/${bus_addr}" "$sysfs_class_node" "sysfs-class"
}


adapter_extra_vpd_hooks="${adapter_extra_vpd_hooks} get_sysfs_mac_address"
# This function retireves the configured MAC address from the specified network device.
get_sysfs_mac_address ()
{
	#Sets key, val, and vpd_function
    key=""
    val=""
    vpd_function=""

    local bus_type=$1
    local bus_addr=$2
    local subtype=$3
    local mac_file
    local sysfs_class_node

    get_sysfs_class_node $bus_type $bus_addr $subtype
    mac_file="${sysfs_class_node}/address"
    if [ -f "$mac_file" ] ; then
        key="NA"
        vpd_function="vpd_field_ensure"
        read val <"$mac_file"
        val=$(echo "$val" | sed -e 's/://g')
    fi
}
	
